home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / ccmd / cmfil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-08-19  |  27.3 KB  |  986 lines

  1. /*
  2.  Author: Howie Kaye
  3.  
  4.  Columbia University Center for Computing Activities, July 1986.
  5.  Copyright (C) 1986, 1987, Trustees of Columbia University in the City
  6.  of New York.
  7.  Permission is granted to any individual or institution to use, copy, or
  8.  redistribute this software so long as it is not sold for profit, provided this
  9.  copyright notice is retained. 
  10. */
  11. /*
  12.  * Code to parse filenames.  Parsing succeeds if current input contains a 
  13.  * delimeter, and and the input up to the delimeter is nonempty, and contains
  14.  * either a uniquely matched filename, or an exactly matched wild or regexp 
  15.  * file spec.  In both of these cases, any other restrictions placed upon the 
  16.  * file (ie it's protection) must be met.  If a new file is being parsed, than
  17.  * either an existing, or a nonexisting filename will parse.
  18.  * Completion succeeds if the string passed to it is an unambiguous match 
  19.  * for a file name, or if it is a valid wildcard filespec.  If a new file is 
  20.  * being parse, no match is necessary.
  21.  * Standard help displays up to `cmcsb._cmmax' filenames in a columnal
  22.  * fashion.  If more filenames than this would be listed, then
  23.  * the number of possible completions is listed instead.
  24.  * The standard break table allows letters, digits, dots, dashes, underscores,
  25.  * tildes, and '#'; wild characters: '*', '?', meta characters: '[', ']',
  26.  * '{', '}'.
  27.  */
  28.                     /* allocate file errors here */
  29. #define FILERR                /* include _CMFIL error symbols */
  30.  
  31. #include "ccmdlib.h"
  32. #include "cmfncs.h"
  33. #include "cmfil.h"
  34. #include "filelist.h"
  35.  
  36. #ifndef NGROUPS
  37. #define NGROUPS 1
  38. #endif
  39.  
  40. #if unix
  41. /*
  42.  * break table for filenames under Unix
  43.  */
  44.                     /* standard break table */
  45. static brktab filbrk = {        /* all valid chars for filenames */
  46.   {                    /* alphanums, "~#/_-\[]" */
  47.     0xff, 0xff, 0xff, 0xff, 0xef, 0xd8, 0x00, 0x3f, 
  48.     0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x1d, 
  49.   },
  50.   {
  51.     0xff, 0xff, 0xff, 0xff, 0xef, 0xd8, 0x00, 0x3f, 
  52.     0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x1d, 
  53.   }
  54. };
  55. #else
  56. #if MSDOS
  57. /*
  58.  * break table for filenames under MSDOS
  59.  */
  60.                     /* standard break table */
  61. static brktab filbrk = {        /* all valid chars for filenames */
  62.   {                    /* alphanums, "~", "#", "/" */
  63.     0xff,  0xff,  0xff,  0xff,  0xf7,  0xc8,  0x00,  0x3f,  
  64.     0x80,  0x00,  0x00,  0x09,  0x80,  0x00,  0x00,  0x1d,  
  65.   },
  66.   {
  67.     0xff,  0xff,  0xff,  0xff,  0xf7,  0xc8,  0x00,  0x3f,  
  68.     0x80,  0x00,  0x00,  0x09,  0x80,  0x00,  0x00,  0x1d,  
  69.   }
  70. };
  71.                     /* wild card break table */
  72. static brktab filwldtab = {        /* all valid chars for filenames */
  73.   {                    /* alphanums, "~", "#", "/" */
  74.     0xff,  0xff,  0xff,  0xff,  0xfb,  0xdc,  0x00,  0x3f,  
  75.     0x80,  0x00,  0x00,  0x17,  0x80,  0x00,  0x00,  0x1f,  
  76.   },
  77.   {
  78.     0xff,  0xff,  0xff,  0xff,  0xfb,  0xdc,  0x00,  0x3f,  
  79.     0x80,  0x00,  0x00,  0x17,  0x80,  0x00,  0x00,  0x1f,  
  80.   }
  81. };
  82.                     /* regex break tab */
  83. static brktab filregtab = {
  84.   {
  85.     0xff,  0xff,  0xff,  0xff,  0xf7,  0xfc,  0x00,  0x3f,  
  86.     0x80,  0x00,  0x00,  0x05,  0x80,  0x00,  0x00,  0x1f  
  87.   },
  88.   {
  89.     0xff,  0xff,  0xff,  0xff,  0xf7,  0xcc,  0x00,  0x3e,  
  90.     0x80,  0x00,  0x00,  0x01,  0x80,  0x00,  0x00,  0x1f  
  91.   },
  92. };
  93. #endif /* MSDOS */
  94. #endif /* !unix */
  95.  
  96. #ifndef MAXPATHLEN
  97. #define MAXPATHLEN 256
  98. #endif
  99.  
  100. #define FNAME(x) (&namebuf[(x)->offset])
  101.  
  102. int fil_parse(), fil_help(), fil_complete();
  103. pvfil buildexactfilelist(),buildpartialfilelist();
  104. char *malloc(),*partial(),*tilde_expand(), *default_ext();
  105.                     /* standard break mask */
  106. ftspec ft_fil =  { fil_parse, fil_help, fil_complete, 0, &filbrk };
  107.  
  108. extern filecount;
  109. extern char *namebuf;
  110. static char *dotpath[] = {".", NULL};
  111. static filblk deffilblk = {        /* default fileblock */
  112.   dotpath,                /* search path */
  113.   NULL,                    /* exceptionspec */
  114.   NULL,                    /* extension */
  115. };
  116.  
  117.  
  118. /*
  119.  * parse routine
  120.  * parse succeeds if:
  121.  * non-wild parse, and one terminated match, or
  122.  * wild parse, and 1 or more matches, or
  123.  * new file parse and 0 or more matches.
  124.  */
  125. PASSEDSTATIC int
  126. fil_parse(text, textlen, fdbp, parselen, value)
  127. char *text;
  128. int textlen, *parselen;
  129. fdb *fdbp;
  130. pval *value;
  131. {
  132.   static char retbuf[200];
  133.   static char *ret[2] = { retbuf, NULL };
  134.   int fcount,ecount,pcount,imatch;    /* files, exact, partial,
  135.                        inv matches */
  136.   dirfile **filelist;            /* list of files */
  137.   char *term;                /* termination character */
  138.   int flags;
  139.   filblk *fblk;
  140.  
  141.   if ((fdbp->_cmffl & FIL_WLD) && (fdbp->_cmffl & FIL_PO))
  142.     return(FILxINV);
  143.  
  144.   flags = fdbp->_cmffl;            /* allow directories too */
  145.   if (fdbp->_cmffl & FIL_EXEC)
  146.     fdbp->_cmffl |= FIL_ASRCH;
  147.                     /* find the number of matches */
  148.   if (textlen == 0)            /* nothing typed */
  149.       return(CMxINC);            /*  so don't do lookup */
  150.  
  151.   fcount = fil_match(text, textlen, fdbp, &filelist, &term, &pcount, &ecount,             &imatch);
  152.  
  153.   fdbp->_cmffl = flags;            /* restore real flags */
  154.  
  155.  
  156.   if (term == NULL) {            /* unterminated. */
  157.     if (ecount == 0 && pcount == 0 && !(flags & FIL_PO))
  158.       return(FILxNM);
  159.     return(CMxINC);            /* pass incompleteness up */
  160.   }
  161.  
  162.   if (ecount == 0) {            /* no matches */
  163.     if (fdbp->_cmffl & FIL_PO) {    /* if new file parse then succeed */
  164.       value->_pvfil = ret;
  165.       if (text[0] == '~') {
  166.       char buf[200]; 
  167.       strncpy(buf, text, term-text);
  168.       buf[term-text]= '\0';
  169.       strcpy(value->_pvfil[0], tilde_expand(buf));
  170.       }
  171.       else {
  172.       strncpy(value->_pvfil[0],text,term-text);
  173.       value->_pvfil[0][term-text] = '\0';
  174.       }
  175.       *parselen = term-text;
  176.       if (validate(value->_pvfil[0]))    /* or fail, if invalid filename */
  177.     return(CMxOK);
  178.       else return(FILxBAD);
  179.     }
  180.     if (pcount > 0) {            /* check again with def extension */
  181.       fblk = (filblk *) fdbp->_cmdat;
  182.       if (fblk == NULL) fblk = &deffilblk;
  183.       if (fblk->def_extension) {
  184.     int i;
  185.     static char buf[MAXPATHLEN];
  186.     for ( i= 0; i < textlen; i++) {
  187.       if (isspace(text[i]&0x7f)) break;
  188.       buf[i] = text[i]&0x7f;
  189.     }
  190.     buf[i] = '\0';
  191.     strcat(buf,default_ext(buf,"",fblk->def_extension,TRUE,filelist,
  192.           fcount));
  193.     *parselen = term - text;
  194.     fcount = fil_match(buf, strlen(buf), fdbp, &filelist, &term, &pcount,
  195.                        &ecount, &imatch);
  196.     if (ecount >= 1) {
  197.       if (iswild(buf))
  198.         return(FILxAMB);
  199.       value->_pvfil = buildexactfilelist(filelist,fcount,1);
  200.       return(CMxOK);
  201.     }
  202.     else return(FILxNM);
  203.       }
  204.       else {
  205.       if (flags & FIL_DIR) {    /* looking for directory names? */
  206.           int i;
  207.           char buf[256];
  208.           /*
  209.            * If input ends in / or consists of ~username, return
  210.            * the directory name
  211.            */
  212.           buf[i = term - text] = 0;
  213.           while (i--)        /* XXX need to strip 8th bit? */
  214.           buf[i] = text[i] & 0x7f;
  215.           i = term - text - 1;    /* look at last character */
  216.           if ((buf[i] == '/') ||
  217.           (buf[0] == '~' && index (buf, '/') == 0)) {
  218.           value->_pvfil = ret;
  219.           strcpy (retbuf, tilde_expand (buf));
  220.           *parselen = term - text;
  221.           return (CMxOK);
  222.           }
  223.       }
  224.       value->_pvfil = buildpartialfilelist(filelist,fcount,pcount);
  225.       return(FILxPMA);        /* return partial match */
  226.       }
  227.     }    
  228.     else return(FILxNM);        /* otherwise, no match */
  229.   }
  230.   else                    /* exact match? */
  231.     if (ecount == 1 || ecount >= 1 && fdbp->_cmffl & FIL_WLD)
  232.       goto success;
  233.   else {                /* multiple matches */
  234.     if (fdbp->_cmffl & FIL_WLD) {    /* wild? */
  235.       value->_pvfil = buildexactfilelist(filelist,fcount,ecount);
  236.       *parselen = term - text;        /* find the length */
  237.       return(CMxOK);
  238.     }
  239.     else {
  240.       static char tbuf[MAXPATHLEN];
  241.       strncpy(tbuf,text,textlen); tbuf[textlen] = '\0';
  242.       if (!iswild(tbuf)) {        /* take first name */
  243.     value->_pvfil = buildexactfilelist(filelist,fcount,1);
  244.     *parselen = term - text;
  245.     return(CMxOK);
  246.       }
  247.       else return(FILxAMB);        /* otherwise ambiguous */
  248.     }
  249.   }
  250. success:                /* a sucessful parse */
  251.   *parselen = term - text;        /* find the length */
  252.   value->_pvfil = buildexactfilelist(filelist,fcount,ecount);
  253.   return(CMxOK);            /* return success */
  254. }
  255.  
  256.  
  257. /*
  258.  * complete:
  259.  * file name completion routine.
  260.  */
  261. PASSEDSTATIC int
  262. fil_complete(text, textlen, fdbp, full, cplt, cpltlen) 
  263. char *text,**cplt;
  264. int textlen,full,*cpltlen;
  265. fdb *fdbp;
  266. {
  267.   int fcount,ecount,pcount,icount;
  268.   dirfile **filelist;
  269.   char *term;
  270.   int flags;
  271.   int i;
  272.   int first;
  273.   int exact;
  274.                     /* find matches */
  275.   *cplt = "";
  276.   *cpltlen = 0;
  277.   flags = fdbp->_cmffl;
  278.   if (fdbp->_cmffl & FIL_EXEC)
  279.     fdbp->_cmffl |= FIL_ASRCH;
  280.  
  281.   fcount = fil_match(text, textlen, fdbp, &filelist, &term, &pcount, &ecount,
  282.              &icount);
  283.   fdbp->_cmffl = flags;
  284.   pcount -= icount;
  285.   ecount -= icount;
  286.   if (ecount >= 1) {            /* exact match.  use it */
  287.     *cpltlen = 0;            /* 0 length completion */
  288.     *cplt = "";                /* point at nothing */
  289.     goto done;                /* and return */
  290.   }
  291.   if (pcount == 0) {            /* no matches. */
  292.     if (textlen > 0) 
  293.       if (fdbp->_cmffl & FIL_PO) {    /* a new file? */
  294.     filblk *fblk;
  295.     fblk = (filblk *) fdbp->_cmdat;
  296.     if (fblk == NULL) fblk = &deffilblk;
  297.  
  298.     if (fblk->def_extension) {
  299.       static char completion[MAXPATHLEN];
  300.       static char tbuf[MAXPATHLEN];
  301.       strncpy(tbuf,text,textlen);
  302.       tbuf[textlen] = '\0';
  303.       strcpy(completion,*cplt);
  304.       strcat(completion,default_ext(tbuf,*cplt,fblk->def_extension,
  305.         TRUE,filelist,fcount));
  306.       if (strcmp(*cplt,completion) || fdbp->_cmffl & FIL_PO) {
  307.         *cplt = completion;
  308.         *cpltlen = strlen(*cplt);
  309.         goto done;
  310.       }
  311.       return(CMP_BEL);
  312.     }
  313.     else {
  314.       *cplt = "";            /* accept unmatching text */
  315.       *cpltlen = 0;
  316.       goto done;
  317.     }
  318.       }
  319.                     /* otherwise */
  320.     *cplt = NULL;            /* return null pointer */
  321.     *cpltlen = 0;
  322.     return(CMP_BEL);            /* take action. */
  323.   }
  324.  
  325.                     /* one or more match */
  326.   *cplt = partial(text,textlen,filelist,fcount,pcount,&exact);
  327.   *cpltlen = strlen(*cplt);
  328.   if (!exact) {
  329.     filblk *fblk;
  330.     fblk = (filblk *) fdbp->_cmdat;
  331.     if (fblk == NULL) fblk = &deffilblk;
  332.  
  333.     if (fblk->def_extension) {
  334.       static char completion[MAXPATHLEN];
  335.       static char tbuf[MAXPATHLEN];
  336.       strncpy(tbuf,text,textlen);
  337.       tbuf[textlen] = '\0';
  338.       strcpy(completion,*cplt);
  339.       strcat(completion,default_ext(tbuf,*cplt,fblk->def_extension,
  340.                          fdbp->_cmffl & FIL_PO,filelist,fcount));
  341.       if (strcmp(*cplt,completion) || (fdbp->_cmffl & FIL_PO)) {
  342.     *cplt = completion;
  343.     *cpltlen = strlen(*cplt);
  344.     goto done;
  345.       }
  346.       return(CMP_BEL);
  347.     }
  348.     return(CMP_BEL);
  349.   }
  350. done:
  351.   if (full) {
  352.     if (fdbp->_cmffl & FIL_NODIR) {
  353.       char *cp;
  354.       int isdir;
  355.       cp = (char *)calloc(textlen+strlen(*cplt)+1, 1);
  356.       strncpy(cp,text,textlen);
  357.       strcat(cp,*cplt);
  358.       isdir = do_stat(tilde_expand(cp)) & FIL_ADR;
  359.       free(cp);
  360.       if (isdir) {
  361.         strcat(*cplt, "/");
  362.     (*cpltlen)++;
  363.         return(CMP_BEL);
  364.       }
  365.     }
  366.     return(CMP_GO | CMP_SPC);
  367.   }
  368.   else return(CMP_PNC);
  369. }
  370.  
  371. /*
  372.  * help routine
  373.  */
  374. PASSEDSTATIC int
  375. fil_help(text, textlen, fdbp, cust) 
  376. char *text;
  377. int textlen, cust;
  378. fdb *fdbp;
  379. {
  380.   int fcount,pcount,ecount,icount;    /* number of files/matches */
  381.   dirfile **d,**filelist;        /* list of them */
  382.   char *term;
  383.   int found,i,j,maxlen,fillen;
  384.   int cols,curcol,putlen;
  385.   char *realdir;
  386.   filblk *fblk;
  387.   if (!cust) {                /* standard help msg */
  388.     cmxputs("filename, one of the following:"); 
  389.   }
  390.   cmxflsh();                /* fil_match may be slow...  */
  391.   fcount = fil_match(text, textlen, fdbp, &filelist, &term, &pcount, &ecount,
  392.              &icount);
  393.   pcount -= icount;
  394.   ecount -= icount;
  395.   if (pcount == 0) {
  396.     cmxnl();
  397.     cmxputs(" (No filenames match current input)"); /* none here */
  398.     return(CMxOK);            /* all done */
  399.   }
  400.   fblk = (filblk *) fdbp->_cmdat;
  401.   if (fblk == NULL) fblk = &deffilblk;
  402.   if (pcount > cmcsb._cmmax) {
  403.     static char buf[MAXPATHLEN];
  404.     sprintf(buf,"  (%d matching filenames)",pcount,cmcsb._cmmax);
  405.     cmxnl();
  406.     cmxputs(buf);
  407.     return(CMxOK);
  408.   }
  409.  
  410.   maxlen = 0;
  411.   found = 0;
  412.   for (i = 0; i < fcount; i++) {
  413.     if ((filelist[i]->flags & (FIL_MAT|FIL_NOR|FIL_INV)) == FIL_MAT) {
  414.       if (fdbp->_cmffl & FIL_NOEXT) 
  415.     fillen = fnamlen(FNAME(filelist[i]));
  416.       else
  417.     fillen = strlen(FNAME(filelist[i]));
  418.       if (!(fdbp->_cmffl & FIL_NOPTH)) {
  419.     fillen += strlen(tilde_expand(filelist[i]->directory));
  420.     if (!index(DIRSEP,
  421.           filelist[i]->directory[strlen(filelist[i]->directory)-1]))
  422.       fillen+=1;
  423.       }
  424.       if (fdbp->_cmffl & FIL_TYPE) fillen += 1;
  425.       if (maxlen < fillen) maxlen = fillen;
  426.       if (++found >= pcount) break;
  427.     }
  428.   }
  429.   maxlen += 3;
  430.   /* XXX see comments in cmkey.c on the subject of multi-column output */
  431.   cols = (cmcsb._cmcmx+2) / maxlen;    /* number of columns per line */
  432.   if (cols <= 0) cols = 1;
  433.   curcol = 0;                /* currently printing first column */
  434.   for (i = 0, found = 0; i < fcount && found < pcount; i++) {
  435.     if ((filelist[i]->flags & (FIL_MAT|FIL_NOR|FIL_INV)) == FIL_MAT) {
  436.       found++;
  437.       putlen = 0;
  438.       if (curcol == 0) {
  439.         cmxnl();            /* new line for first column */
  440.         cmxputs(" ");            /* and offset a bit */
  441.       }
  442.       if (!(fdbp->_cmffl & FIL_NOPTH)) {
  443.         if (strcmp(filelist[i]->directory,".")) {
  444.       realdir = tilde_expand(filelist[i]->directory);
  445.         cmxputs(realdir);
  446.       if (!index(DIRSEP,realdir[strlen(realdir)-1])) {
  447.         cmxputc('/');
  448.         putlen++;
  449.       }
  450.           putlen+= strlen(realdir);
  451.         }
  452.     else realdir = filelist[i]->directory;
  453.       }
  454.       if (!(fdbp->_cmffl & FIL_NOEXT)) {
  455.         cmxputs(FNAME(filelist[i]));
  456.         putlen += strlen(FNAME(filelist[i]));
  457.       }
  458.       else {
  459.         int l = fnamlen(FNAME(filelist[i]));
  460.         int k;
  461.         for (k = 0; k < l; k++) cmxputc(FNAME(filelist[i])[k]);
  462.         putlen += l;
  463.       }
  464.      if (fdbp->_cmffl & FIL_TYPE) {
  465.     if (!(filelist[i]->flags & FIL_STAT)) {
  466.       static char fname[MAXPATHLEN];
  467.       strcpy(fname,realdir);
  468.       if (strlen(realdir) != 0)
  469.         if (fname[strlen(fname)-1] != '/')
  470.           strcat(fname,"/");
  471.       strcat(fname,FNAME(filelist[i]));
  472.       filelist[i]->flags |= do_stat(fname);
  473.     }
  474.     if (filelist[i]->flags & FIL_ADR)
  475.       cmxputc('/');
  476.     else if (filelist[i]->flags & FIL_ALK)
  477.       cmxputc('@');
  478.     else if (filelist[i]->flags & FIL_AEX)
  479.       cmxputc('*');
  480.     else cmxputc(' ');
  481.     putlen++;
  482.       }
  483.       if (curcol < (cols-1)) {        /* space out if not last column */
  484.     int j;
  485.         for (j = putlen; j < maxlen; j++)
  486.       cmxputc(SPACE);
  487.       }
  488.       curcol = (curcol+1) % cols;    /* and move to next column */
  489.     }
  490.   }
  491.   /* cmxnl(); */
  492.   return(CMxOK);            /* all done */
  493. }
  494.  
  495. PASSEDSTATIC int
  496. fil_match(text, textlen, fdbp, mat, term,pmatch,ematch,imatch) 
  497. char *text,**term;            /* termination character */
  498. int textlen;                /* length of text */
  499. fdb *fdbp;                /* pointer to fdb */
  500. dirfile ***mat;                /* list of matching files */
  501. int *pmatch,*ematch,*imatch;        /* partial, and exact match counts */
  502. {
  503.   static char dirname[MAXPATHLEN], fname[MAXPATHLEN];
  504.   int i,j;
  505.   char **path;
  506.   char *xpath[2];
  507.   char *suffix = NULL;
  508.   filblk *fblk;
  509.   int fcount;
  510.   static char nambuf[MAXPATHLEN];
  511.   int namlen;
  512.   dirfile *df,**search_path(),**m;
  513.   int access_flags,access;
  514.   int e,p,toklen;
  515.   brktab *btab;                /* break table to use */
  516.   
  517.  
  518.   if ((btab = fdbp->_cmbrk) == NULL)    /* get supplied break table */
  519.     btab = &filbrk;            /* or use default */
  520.  
  521.   xpath[0] = dirname;
  522.   xpath[1] = NULL;
  523.  
  524.   fblk = (filblk *) fdbp->_cmdat;
  525.   if (fblk == NULL) fblk = &deffilblk;
  526.  
  527.  
  528.   dirname[0] = fname[0] = '\0';        /* initially no name */
  529.  
  530.   for( i= 0; i < textlen; i++) 
  531.     if (BREAK(btab,text[i],i))
  532.       break;
  533.   toklen = i;
  534.  
  535.   for(i = toklen-1; i >= 0; i--) {    /* find final part of name */
  536.     if (index(DIRSEP,text[i])) break;
  537.   }
  538.  
  539.   path = fblk->pathv;            /* so use default path passed us */
  540.   if (path == NULL) {            /* and if none, use "." */
  541.     path = dotpath;      
  542.   }
  543.  
  544.   if (i >= 0) {                /* found a directory  */
  545.     for(j = 0; j < i; j++)        /* separate out directory */
  546.       dirname[j] = text[j] & 0x7f;    /* and convert to 7 bit */
  547. #ifdef undef
  548.     if (j == 0) strcpy(dirname,"/");    /* check for just a "/" */
  549.     else dirname[j] = '\0';        /* null terminate */
  550. #else
  551.     dirname[j] = '\0';
  552. #endif
  553.     if (index(STRUCTTERM,text[i])) 
  554.       strcat(dirname,":");
  555.     if (index(STRUCTTERM,text[i-1]))
  556.       strcat(dirname,"/");
  557.     suffix = xpath[0];            /* and use this as the search path */
  558.   }
  559.   else {
  560.       /* XXX make "~" work without trailing "/" */
  561.  
  562.       if ((text[0] & 0x7f) == '~') {
  563.       int n;
  564.       for (n=0; n < toklen; n++)
  565.           if ((text[n] & 0x7f) != '/')
  566.           dirname[n] = text[n] & 0x7f;
  567.           else
  568.           break;
  569.       dirname[n] = 0;
  570.       if (n =! toklen)
  571.           dirname[0] = 0;        /* back out */
  572.       else {
  573.           suffix = xpath[0];    /* use ~ or ~user as dir name */
  574.           i = toklen - 1;        /* fudge for code below */
  575.       }
  576.       }
  577.   }
  578.   *term = NULL;                /* and find termination char */
  579.   for(j = i+1; j < textlen; j++)    /* separate out the filename */
  580.     if (!isspace(text[j]&0x7f))
  581.       fname[j-(i+1)] = text[j] & 0x7f;    /* and convert to 7 bit */
  582.     else {
  583.       *term = &text[j];
  584.       break;
  585.     }
  586.  
  587.   fname[j-(i+1)] = '\0';        /* and null terminate */
  588.  
  589.   m = search_path(path,suffix,&fcount);    /* search the directories */
  590.   
  591.   *pmatch = 0;                /* no matches so far */
  592.   *ematch = 0;
  593.   *imatch = 0;
  594.  
  595.   access_flags = fdbp->_cmffl & FIL_ALL;/* extract file access codes */
  596.  
  597.   for (i = 0; i < fcount; i++) {
  598.     e = p = 0;
  599.     df = m[i];                /* current file */
  600.     df->flags &= ~(FIL_MAT|FIL_EXA|FIL_INV|FIL_NOR);
  601.     strcpy(nambuf,tilde_expand(df->directory));
  602.     if (nambuf[strlen(nambuf)-1] != '/')
  603.       strcat(nambuf,"/");
  604.     namlen = strlen(nambuf);
  605.  
  606.     if (fmatch(FNAME(df),fname,TRUE)) {    /* partially matching? */
  607.       df->flags |= FIL_MAT;        /* mark it matching */
  608.       p = 1;                /* flag a match */
  609.     }
  610.  
  611.     if (fmatch(FNAME(df),fname,FALSE)) { /* exact matching? */
  612.       df->flags |= FIL_EXA;        /* yup...mark it */
  613.       e = 1;                /* and flag it */
  614.     }
  615.  
  616.     if (access_flags == 0) {
  617.       if (e) (*ematch)++;
  618.       if (p) {
  619.     (*pmatch)++;
  620.     if (fblk->exceptionspec != NULL) /* if in exception list */
  621.       if(fmatch(FNAME(df),fblk->exceptionspec,FALSE)) {
  622.         df->flags |= FIL_INV;        /* then ignore it */
  623.         (*imatch)++;
  624.       }
  625.       }
  626.       continue;
  627.     }
  628.     else if (e || p) {            /* if match, stat it */
  629.       if (!(df->flags & FIL_STAT)) {    /* was it stat'ed yet? */
  630.     strcpy(&nambuf[namlen],FNAME(df)); /* complete the name */
  631.     df->flags |= do_stat(nambuf);
  632.       }
  633.       access = FALSE;            /* check access flags */
  634.       if (access_flags & FIL_EXEC && df->flags & FIL_AEX)
  635.     access = TRUE;
  636.       else
  637.       if (access_flags & FIL_RD && df->flags & FIL_ARD)
  638.     access = TRUE;
  639.       else
  640.       if (access_flags & FIL_WR && df->flags & FIL_AWR)
  641.     access = TRUE;
  642.       else
  643.       if (access_flags & FIL_HID && df->flags & FIL_AHD)
  644.     access = TRUE;
  645.       else
  646.       if (access_flags & FIL_SYS && df->flags & FIL_ASY)
  647.     access = TRUE;
  648.       else
  649.       if (access_flags & FIL_EXEC && df->flags & FIL_AEX)
  650.     access = TRUE;
  651.       else
  652.       if (access_flags & FIL_DIR && df->flags & FIL_ADR)
  653.     access = TRUE;
  654.       if (access) {
  655.     if (e) (*ematch)++;
  656.     if (p) {            /* a match */
  657.       (*pmatch)++;            /* count it */
  658.       if (fblk->exceptionspec != NULL) /* if in exception list */
  659.         if(fmatch(FNAME(df),fblk->exceptionspec,FALSE)) {
  660.           df->flags |= FIL_INV;    /* make it invisible */
  661.           (*imatch)++;
  662.         }
  663.     }
  664.       }
  665.       else df->flags |= FIL_NOR;    /* ignore it */
  666.     }
  667.   }
  668.   *mat = m;
  669.   return(fcount);
  670. }
  671.  
  672. /*
  673.  * build a list of all matching, unignored files.
  674.  * matchtype can be either exact or partial
  675.  */
  676.  
  677. PASSEDSTATIC pvfil
  678. buildfilelist(fl,count,matches,matchtype) dirfile **fl; int count,matches; {
  679.   static char **retv=NULL;
  680.   static int length=0;
  681.   int i,j;
  682.   static char dir[MAXPATHLEN];
  683.  
  684.   if (retv != NULL) {            /* free up previous values */
  685.     for(i = 0; i < length; i++) free(retv[i]);
  686.     free(retv);
  687.   }
  688.   length = matches;
  689.  
  690.                     /* build list */
  691.   retv = (char **) malloc(sizeof(char *) * (matches + 1));
  692.                     /* find matches */
  693.   for(i = 0,j = 0; i < count && j < matches; i++) {
  694.     if ((fl[i]->flags & (matchtype|FIL_NOR)) == matchtype) {
  695.       int len;
  696.       strcpy(dir,tilde_expand(fl[i]->directory));
  697.       if (index(DIRSEP,dir[strlen(dir)-1])) { /* if it has a '/' */
  698.      retv[j] = malloc(sizeof(char)*(strlen(FNAME(fl[i]))+strlen(dir)+1));
  699.     strcpy(retv[j],dir);        /* copy directory */
  700.     strcat(retv[j],FNAME(fl[i]));    /* and filename */
  701.       }
  702.       else 
  703.     if (strcmp(dir,"."))  {        /* no slash.   copy name and insert */
  704.       retv[j] = malloc(sizeof(char)*(strlen(FNAME(fl[i]))+strlen(dir)+2));
  705.       strcpy(retv[j],dir);
  706.       strcat(retv[j],"/");
  707.       strcat(retv[j],FNAME(fl[i]));
  708.     }
  709.       else {                /* in "." don't use directory */
  710.     retv[j] = malloc((strlen(FNAME(fl[i])) + 1) * sizeof(char *));
  711.     strcpy(retv[j],FNAME(fl[i]));
  712.       }
  713.       j++;                /* for all files */
  714.     }
  715.   }
  716.   retv[matches] = NULL;            /* null terminate vector */
  717.   return(retv);
  718. }
  719.  
  720. /*
  721.  * build list of partially matching files
  722.  */
  723. PASSEDSTATIC pvfil
  724. buildpartialfilelist(fl,count,matches)
  725. dirfile **fl; int count,matches; 
  726. {
  727.   return(buildfilelist(fl,count,matches,FIL_MAT));
  728. }
  729.  
  730. /*
  731.  * build list of exactly matching files
  732.  */
  733. PASSEDSTATIC pvfil
  734. buildexactfilelist(fl,count,matches)
  735. dirfile **fl; int count,matches; 
  736. {
  737.   return(buildfilelist(fl,count,matches,FIL_EXA));
  738. }
  739.  
  740. /*
  741.  * get length of filename up to the extension
  742.  * skip leading dots
  743.  */
  744. PASSEDSTATIC int
  745. fnamlen(nam) char *nam; {
  746.   register int i;
  747.   for (i = 0; i < strlen(nam); i++)    /* skip leading dot's */
  748.     if (nam[i] != '.') break;
  749.   for (i = 1; i < strlen(nam); i++)    /* stop at final dot */
  750.     if (nam[i] == '.') break;
  751.   return(i);
  752. }
  753.  
  754.  
  755. /*
  756.  * find a partial completion for a list of files
  757.  */
  758.  
  759. PASSEDSTATIC char *
  760. partial(text,textlen,filelist,fcount,pcount,exact) 
  761. char *text; 
  762. int textlen;
  763. dirfile **filelist;
  764. int fcount,pcount;
  765. int *exact;
  766. {
  767.   int i,j,k;
  768.   static char buf[MAXPATHLEN];
  769.   static char tbuf[MAXPATHLEN],fbuf[MAXPATHLEN],fname[MAXPATHLEN];
  770.   int buflen,fbuflen;
  771.  
  772.   *exact = TRUE;            /* assume we find an exact match */
  773.   for(i = textlen-1; i >= 0; i--)    /* get the name */
  774.     if (index(DIRSEP,text[i])) break;
  775.  
  776.   for(j = i+1; j < textlen; j++)
  777.     tbuf[j-(i+1)] = text[j] & 0x7f;    /* copy text */
  778.   tbuf[j-(i+1)] = '\0';            /* null terminate it */
  779.        
  780.   buf[0] = '\0';            /* start off with no matches */
  781.   for(i = 0, j = 0; i < fcount && j < pcount; i++) {
  782.     if ((filelist[i]->flags & (FIL_MAT|FIL_INV|FIL_NOR)) == FIL_MAT) {
  783.       strcpy(fbuf,FNAME(filelist[i]));
  784.       fbuflen = strlen(fbuf);        /* copy then name */
  785.       while(!fmatch(fbuf,tbuf,FALSE)) {    /* shorten until we match */
  786.     fbuf[--fbuflen] = '\0';
  787.       }
  788.       strcpy(fname,FNAME(filelist[i]));
  789.       if (j++ == 0)
  790.     strcpy(buf,&fname[fbuflen]);    /* first time, grab completion */
  791.       else {
  792.     buflen = strlen(buf);
  793.     for(k = 0; k < buflen; k++)    /* otherwise trim it to match */
  794.       if (buf[k] != fname[fbuflen+k]) {
  795.         buf[k] = '\0';        /* if end of a name, then exact */
  796.         if (fname[fbuflen+k] != '\0') *exact = FALSE;
  797.         else *exact = TRUE;
  798.         break;
  799.       }
  800.       }
  801.     }
  802.   }
  803.   return(buf);
  804. }
  805.  
  806. /*
  807.  * based on current type in, return part of default extension to 
  808.  * append to filename
  809.  */
  810.  
  811. PASSEDSTATIC char *
  812. default_ext(fname, cplt, ext, new,filelist,fcount)
  813. char *fname,*cplt,**ext;
  814. int new;
  815. dirfile **filelist;
  816. int fcount;
  817. {
  818.   int i,j,k,l;
  819.   int start;
  820.   static char buf[MAXPATHLEN],*e;
  821.   if (ext == NULL) return ("");
  822.   strcpy(buf,fname);            /* get name */
  823.   strcat(buf,cplt);            /* append completion */
  824.   for(start = 0; start < strlen(buf); i++) /* skip leading dots */
  825.     if (buf[start] != '.') break;
  826.   for(i = strlen(buf)-1; i >= start; i--) /* look for existing extension */
  827.     if (buf[i] == '.') break;
  828.   if (i <= start) i = strlen(buf);    /* none.  point to end of string */
  829.   if (!new)                /* if existing file  */
  830.     if (buf[i] == '\0') return("");    /* and no "." inserted, all done */
  831.   for(e=ext[0],k = 0; ext[k] != NULL; k++,e = ext[k]) {    /* for all extensions*/
  832.     for(j = i; j < strlen(buf); j++) {    /* check for a match */
  833.       if (buf[j] != e[j-i]) {        /* no match.  try next extension */
  834.     break;
  835.       }
  836.     }
  837.     if (j == strlen(buf)) {        /* success... */
  838.       static char tbuf[MAXPATHLEN];    /* see how much of the extension */
  839.       strcpy(tbuf,buf);            /* we need to use */
  840.       strcat(tbuf,&e[j-i]);        /* cuz some may be typed already */
  841.       for (l = 0; l < fcount; l++)
  842.     if (filelist[l]->flags & FIL_MAT) 
  843.       if (fmatch(FNAME(filelist[l]),tbuf,FALSE)) 
  844.         return(&e[j-i]);
  845.     }
  846.   }
  847.   if (ext[k] == NULL) {            /* no matches.  if new ok */
  848.     if (new) {
  849.       for(start = 0; start < strlen(buf); i++)
  850.     if (buf[start] != '.') break;
  851.       for(i = strlen(buf)-1; i >= start; i--) /* use first extension */
  852.     if (buf[i] == '.') break;    /* find the . */
  853.       if (i <= start) i = strlen(buf);    /* no dot.  goto end */
  854.       for(j = i; j < strlen(buf); j++) { /* check for a match */
  855.     if (buf[j] != ext[0][j-i]) {    /* no match.  */
  856.       return("");            /* fail */
  857.     }
  858.       }
  859.       return(&ext[0][j-i]);        /* return extension */
  860.     }
  861.     else return("");            /* nothing to do */
  862.   }
  863. }
  864.  
  865. /*
  866.  * validate a filename
  867.  * for now, in MSDOS, just check the length
  868.  */
  869. PASSEDSTATIC validate(fname) char *fname; {
  870.  
  871. #ifdef unix
  872.   return(TRUE);                /* anything goes */
  873. #endif
  874. #ifdef MSDOS
  875. /*
  876.  * for now, just check by length
  877.  */
  878.  
  879.   int i,j;
  880.   for(i = 0; i < strlen(fname); i++) 
  881.     if (fname[i] == '.') break;
  882.   if (i > 8) return(FALSE);
  883.   for(j = i+1; j < strlen(fname); j++);
  884.     if (j - i > 4) return(FALSE);
  885.   return(TRUE);
  886. #endif
  887. }
  888.  
  889.  
  890. /*
  891.  * get info on a file.
  892.  * need to know if it is a link, a directory, and the user's access to it
  893.  */
  894. PASSEDSTATIC 
  895. do_stat(name)
  896. char *name;
  897. {
  898.   struct stat statbuf;
  899.   int flags=0;
  900. #if unix
  901.   int i;
  902.   int uid,mgroups,gid[NGROUPS];        /* hold our groups */
  903.   int ngroups = NGROUPS;
  904.  
  905. #ifdef S_IFLNK
  906.   if (!lstat(name,&statbuf)) {        /* do a stat */
  907.     flags |= FIL_STAT;            /* mark it stat()'ed */
  908.     if ((statbuf.st_mode & S_IFLNK) == S_IFLNK) {
  909.       flags |= FIL_ALK;            /* if a link, note it */
  910.       stat(name,&statbuf);        /* and get the real info */
  911.     }
  912. #else
  913.   if (!stat(name,&statbuf)) {
  914.     flags |= FIL_STAT;
  915. #endif /* S_IFLNK */
  916.  
  917. /*
  918.  * check file protection.  check world first.  then owner,
  919.  * then group
  920.  */
  921.     if(statbuf.st_mode & S_IFDIR)
  922.       flags |= FIL_ADR;
  923.     if (statbuf.st_mode & 4)        /* world read? */
  924.       flags |= FIL_ARD;
  925.     if (statbuf.st_mode & 2)        /* world write? */
  926.       flags |= FIL_AWR;
  927.     if (statbuf.st_mode & 1)        /* world execute? */
  928.       flags |= FIL_AEX;
  929.  
  930.     if ((statbuf.st_mode & 7) != 7) {    /* still don't know...check owner */
  931.       uid = geteuid();            /* get effective uid */
  932.       if (uid == statbuf.st_uid) {    /* am i the owner? */
  933.     if (statbuf.st_mode & S_IREAD)    /* owner read? */
  934.       flags |= FIL_ARD;
  935.     if (statbuf.st_mode & S_IWRITE)    /* owner write? */
  936.       flags |= FIL_AWR;
  937.     if (statbuf.st_mode & S_IEXEC)    /* owner execute? */
  938.       flags |= FIL_AEX;
  939.                     /* read and write */
  940.       }
  941.                     /* still don't know.  check group */
  942.       if ((flags & (FIL_ARD|FIL_AWR|FIL_AEX)) != (FIL_ARD|FIL_AWR|FIL_AEX)) {
  943. #if NGROUPS > 1
  944.     getgroups(ngroups,gid);
  945. #else
  946.     gid[0] = getgid();
  947. #endif
  948.     for(i= 0; i < ngroups; i++) 
  949.       if (gid[i] == statbuf.st_gid) {
  950.         if (statbuf.st_mode & 040)    /* group read? */
  951.           flags |= FIL_ARD;
  952.         if (statbuf.st_mode & 020)    /* group write? */
  953.           flags |= FIL_AWR;
  954.         if (statbuf.st_mode & 010)    /* group execute? */
  955.           flags |= FIL_AEX;
  956.         break;
  957.       }
  958.     if (flags & FIL_ARD && flags & FIL_AWR)
  959.         flags |= FIL_ARW;
  960.       }
  961.     }
  962.   }
  963. #endif /* unix */
  964. #ifdef MSDOS
  965.   if (!stat(name,&statbuf)) {
  966.     flags = FIL_STAT;
  967.     if (statbuf.st_mode & S_IFDIR)
  968.       flags |= FIL_ADR;
  969.     if (statbuf.st_mode & S_IREAD)
  970.       flags |= FIL_ARD;
  971.     if (statbuf.st_mode & S_IEXEC)
  972.       flags |= FIL_AEX;
  973.     if (statbuf.st_mode & S_IWRITE)
  974.       flags |= FIL_AWR;
  975.     if (flags & FIL_ARD && flags & FIL_AWR)
  976.     flags |= FIL_ARW;
  977.   }
  978. #endif /* MSDOS */
  979.                     /* if dir and execute */
  980.   if (flags & (FIL_ADR|FIL_AEX) == (FIL_ADR|FIL_AEX)) {
  981.     flags &= ~FIL_AEX;
  982.     flags |= FIL_ASRCH;            /* then turn on search bit instead */
  983.   }
  984.   return(flags);
  985. }
  986.